package org.store.service;


import org.store.dao.CategoryDao;
import org.store.dao.DaoException;
import org.store.domain.Category;
import org.store.domain.Product;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class CategoriesHolder {
    private CategoryDao categoryDao;
    private Map<Long, Category> categories;
    private List<Category> parentCategories = new ArrayList<>();
    private final AtomicBoolean initialized = new AtomicBoolean();

    public void init() throws DaoException {
        if (initialized.compareAndSet(false, true)) {
            categories = categoryDao.getCategoryMap();
            for (Category category : categories.values()) {
                if (category.getParent() == null) {
                    parentCategories.add(category);
                }
            }
        }
    }

    public Map<Long, Category> getCategories() throws DaoException {
        init();
        return categories;
    }

    public Category getCategory(Product product) throws DaoException {
        init();
        return categories.get(product.getCategoryId());
    }

    public List<Category> getParentCategories() throws DaoException {
        init();
        return parentCategories;
    }

    public void setCategoryDao(CategoryDao categoryDao) {
        this.categoryDao = categoryDao;
    }

    public List<Category> getSubCategories(long categoryId) throws DaoException {
        init();
        //todo return empty list if category doesn't exist
        try {
            Category category = categories.get(categoryId);

            List<Category> parent = Arrays.asList(category);
            List<Category> result = new ArrayList<>(parent);
            Queue<Category> queue = new ArrayDeque<>(parent);

            while (!queue.isEmpty()) {
                Category c = queue.poll();
                result.addAll(c.getSubcategories());
                queue.addAll(c.getSubcategories());
            }
            return result;

        } catch (NullPointerException e) {
            return new ArrayList<>();
        }
    }

}
